package top.v5it.japi.plus.core.spring.web.interceptor;

import cn.hutool.core.map.MapUtil;
import org.springframework.data.redis.core.RedisTemplate;
import top.v5it.japi.plus.common.ConstantCommon;
import top.v5it.japi.plus.common.func.MethodTemplate;
import top.v5it.japi.plus.common.util.AssertData;
import top.v5it.japi.plus.core.ConstantPool;
import top.v5it.japi.plus.core.cache.JapiCache;
import top.v5it.japi.plus.core.exception.JapiAuthorizedException;
import top.v5it.japi.plus.core.util.HttpServletUtil;
import top.v5it.japi.plus.log.OperLogService;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;

import static top.v5it.japi.plus.core.api.ResultStatus.TOKEN_AUTH;

/**
 * token检验
 *
 * @author zhanpu
 * @date 2022/3/3
 */
public class JapiTokenInterceptor extends AbstractJapiInterceptor {

    private final JapiCache japiCache;

    /**
     * 构造函数
     *
     * @param redisTemplate  {@link RedisTemplate}
     * @param operLogService {@link OperLogService}
     */
    public JapiTokenInterceptor(JapiCache japiCache, OperLogService operLogService) {
        super(operLogService);
        this.japiCache = japiCache;
    }

    /**
     * 重写，空实现
     *
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    }

    /**
     * 验证token，可以通过两种形式进行token传递：
     * <p>
     * 1、通过url请求地址传递
     * <p>
     * 2、通过设置head头传递
     * <p>
     * 建议一个项目只选择其中一种形式，不要混合以上两者一起使用
     *
     * @param request   {@link HttpServletRequest}
     * @param response  {@link HttpServletResponse}
     * @param startTime 开始时间
     * @throws Exception 异常
     */
    @Override
    protected void preHandle(HttpServletRequest request, HttpServletResponse response, long startTime) throws Exception {
        final String token = getToken(request);
        // 改用String结构，方便自动过期
        final Object objV = japiCache.getRedisTemplate()
                .opsForValue()
                .get(ConstantPool.getCacheTokenName() + ConstantCommon.COLON + token);

        AssertData.notBlankIfStr(objV
                , () -> new JapiAuthorizedException(TOKEN_AUTH, "请求头[{}]或请求地址[{}]无效", ConstantPool.getHeadTokenName(), ConstantPool.TOKEN_NAME));
    }

    @Override
    protected void prePostHandle(HttpServletRequest request, HttpServletResponse response, Map<String, String> authMap, long startTime) throws Exception {
    }

    /**
     * 获取token
     *
     * @param request {@link HttpServletRequest}
     * @return token信息
     */
    private String getToken(HttpServletRequest request) {
        return MethodTemplate.executeWithFunc0(() -> HttpServletUtil.getToken(request)
                , () -> {
                    Map<String, Object> pathVariables = HttpServletUtil.getPathVariables(request);
                    AssertData.isTrue(pathVariables.containsKey(ConstantPool.TOKEN_NAME)
                            , () -> new JapiAuthorizedException(TOKEN_AUTH, "[{}]不能为空", ConstantPool.TOKEN_NAME));
                    return MapUtil.getStr(pathVariables, ConstantPool.TOKEN_NAME);
                });
    }
}
